package com.dss.auto_deploy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dss.auto_deploy.config.WebsocketEndPoint;
import com.dss.auto_deploy.constants.AutoConstants;
import com.dss.auto_deploy.domain.AdmDocker;
import com.dss.auto_deploy.domain.AdmDockerRegistry;
import com.dss.auto_deploy.domain.Server;
import com.dss.auto_deploy.dto.*;
import com.dss.auto_deploy.mapper.AdmDockerMapper;
import com.dss.auto_deploy.mapper.AdmDockerRegistryMapper;
import com.dss.auto_deploy.service.IAdmDockerService;
import com.dss.auto_deploy.service.IServerService;
import com.dss.auto_deploy.util.*;
import com.dss.auto_deploy.vo.AdmDockerVo;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Container;
import com.jcraft.jsch.Session;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
* @Description:docker界面地址Service实现类
* @author dongshoushan
* @date 2022-09-22 14:38:54
*/
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class AdmDockerServiceImpl  extends ServiceImpl<AdmDockerMapper,AdmDocker> implements IAdmDockerService {

    /**
    *
    * docker界面地址Mapper
    */
    private final AdmDockerMapper admDockerMapper;
    private final AdmDockerRegistryMapper admDockerRegistryMapper;
    private final IServerService serverService;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final RemoteSshUtil remoteSshUtil;
    private final DockerUtil dockerUtil;
    private final RestTemplate restTemplate;
    private final WebsocketEndPoint websocketEndPoint;
    private final CacheUtils cacheUtils;
    /**
     * 私服仓库路径
     */
    private final String daemonPath = "/etc/docker";
    /**
     * 配置文件路径
     */
    private final String servicePath = "/usr/lib/systemd/system";

    /**
    * 新增docker界面地址
    * @param admDockerAddDto
    * @return Long
    */
    @Override
    public int insert(AdmDockerAddDto admDockerAddDto){
        AdmDocker admDocker = BeanUtils.copy(admDockerAddDto, AdmDocker.class);
        admDocker.setCreateTime(new Date());
        admDocker.setUpdateTime(new Date());
        int insert = admDockerMapper.insert(admDocker);
        if (insert == 1) {
            log.info("新增成功{}", JacksonUtils.toString(admDocker));
        }
        cacheUtils.deleteObject("getAllAdmDocker");
        return insert;
    }


    /**
    * 修改docker界面地址
    * @param admDockerUpdateDto
    * @return Long
    */
    @Override
    public int update(AdmDockerUpdateDto admDockerUpdateDto){
        AdmDocker admDocker = BeanUtils.copy(admDockerUpdateDto, AdmDocker.class);
        int number = admDockerMapper.updateById(admDocker);
        if (number == 1) {
            log.info("修改成功{}",JacksonUtils.toString(admDocker));
        }
        cacheUtils.deleteObject("getAllAdmDocker");
        return number;
    }


    /**
    * 批量删除docker界面地址
    * @param ids
    * @return Long
    */
    @Override
    public void deleteBatch(Long[] ids){
        this.removeByIds(Arrays.asList(ids));
        cacheUtils.deleteObject("getAllAdmDocker");
    }

    /**
    * docker界面地址分页查询
    *
    * @param pageDto
    * @return List<AdmDocker>
    */
    @Override
    public Page<AdmDockerVo> list(AdmDockerPageDto pageDto){
        Page<AdmDocker> tablesPage = new Page<>(pageDto.getPage(), pageDto.getLimit());
        QueryWrapper<AdmDocker> queryWrapper = Wrappers.query();
        if (StringUtils.isNotEmpty(pageDto.getContent())){
            queryWrapper.like("docker_name",pageDto.getContent());
        }
        queryWrapper.orderByAsc("docker_name");
        Page<AdmDocker> pageSelectResult = admDockerMapper.selectPage(tablesPage, queryWrapper);
        List<AdmDocker> records = pageSelectResult.getRecords();
        List<AdmDockerVo> voList = convertToVo(records);
        Page<AdmDockerVo> page = new Page<>();
        page.setRecords(voList);
        page.setTotal(pageSelectResult.getTotal());
        page.setSize(pageSelectResult.getSize());
        return page;
    }


    @Override
    public List<AdmDocker> getAll(String name) {
        List<AdmDocker> admDockers = getAll();
        if (StringUtils.isNotEmpty(name)){
            return admDockers.stream().filter(admDocker -> admDocker.getDockerName().contains(name)).collect(Collectors.toList());
        }
        return admDockers;
    }

    @Override
    public List<AdmDocker> getAll() {
        if (cacheUtils.hasKey("getAllAdmDocker")) {
            return cacheUtils.getCacheObject("getAllAdmDocker");
        }else {
            List<AdmDocker> list = admDockerMapper.selectList(null);
            cacheUtils.setCacheObject("getAllAdmDocker", list,60*60*1000*24);
            return list;
        }
    }

    @Override
    public AdmDocker getByIp(String ip) {
        Server server = serverService.getServerByIp(ip);
        QueryWrapper<AdmDocker> queryWrapper = Wrappers.query();
        queryWrapper.eq("server_id",server.getId());
        AdmDocker admDocker = admDockerMapper.selectOne(queryWrapper);
        return admDocker;
    }

    @Override
    public AdmDocker getByRegistryId(Long registryId) {
        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(registryId);
        Long serverId = admDockerRegistry.getServerId();
        QueryWrapper<AdmDocker> queryWrapper = Wrappers.query();
        queryWrapper.eq("server_id",serverId);
        AdmDocker admDocker = admDockerMapper.selectOne(queryWrapper);
        return admDocker;
    }

    @Override
    public void install(DockerInstallDto installDto) {
        long dockerFileSize = installDto.getDockerFile().getSize();
        long portainerFileSize = installDto.getPortainerFile().getSize();
        String tempPath = installDto.getTempPath();
        String ids = installDto.getIds();
        String[] split = ids.split(",");
        CountDownLatch mainLatch = new CountDownLatch(split.length);
        for (String id : split) {
            threadPoolTaskExecutor.execute(() -> {
                AdmDocker admDocker = admDockerMapper.selectById(id);
                Server serverDocker = serverService.getServerById(admDocker.getServerId());
                //上传包
                try {
                    Session session = remoteSshUtil.connect(serverDocker);
                    System.out.println("开始上传docker文件");
                    //关闭防火墙
                    remoteSshUtil.exec(AutoConstants.DISABLE_FIREWALLD, session);
                    remoteSshUtil.exec(AutoConstants.STOP_FIREWALLD,session);
                    remoteSshUtil.exec(AutoConstants.AUTHORIZATION_FOLDER+tempPath,session);
                    CountDownLatch mainLatch2 = new CountDownLatch(2);
                    threadPoolTaskExecutor.execute(() -> {
                        try {
                            remoteSshUtil.putFile(session,installDto.getDockerFile(),tempPath,dockerFileSize,Long.parseLong(id),"dockerInstall",serverDocker.getIp());
                        } catch (Exception e) {
                            throw new ServiceException(e.getMessage());
                        }finally {
                            mainLatch2.countDown();
                        }
                    });
                    threadPoolTaskExecutor.execute(() -> {
                        try {
                            remoteSshUtil.putFile(session,installDto.getPortainerFile(),tempPath,portainerFileSize,Long.parseLong(id),"dockerInstall",serverDocker.getIp());
                        } catch (Exception e) {
                            throw new ServiceException(e.getMessage());
                        }finally {
                            mainLatch2.countDown();
                        }
                    });
                    mainLatch2.await();
                    Thread.sleep(3000L);
                    sendMsg("installing",installDto.getDockerFile().getOriginalFilename());
                    sendMsg("installing",installDto.getPortainerFile().getOriginalFilename());
                    Map map = new HashMap<String,Object>(16);
                    //配置内网仓库
                    if (installDto.getRegistryId() != null){
                        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(installDto.getRegistryId());
                        Server serverRegistry = serverService.getServerById(admDockerRegistry.getServerId());
                        map.put("registryHost",serverRegistry.getIp()+":"+admDockerRegistry.getPort());
                        String daemonLocalPath = AutoConstants.baseLocalPath+admDocker.getId()+"/daemon.json";
                        //生成daemon.json
                        FileUtils.generateFileByTemplate("daemon-json.ftl", daemonLocalPath,AutoConstants.baseLocalPath+admDocker.getId(),map);
                        remoteSshUtil.putFile(session, daemonLocalPath,daemonPath);
                    }
                    //生成service
                    String serviceLocalPath =AutoConstants.baseLocalPath+admDocker.getId()+"/docker.service";
                    map.put("port",admDocker.getPort().toString());
                    FileUtils.generateFileByTemplate("docker-service.ftl", serviceLocalPath,AutoConstants.baseLocalPath+admDocker.getId(),map);
                    remoteSshUtil.exec(AutoConstants.AUTHORIZATION_FOLDER+servicePath,session);
                    remoteSshUtil.putFile(session, serviceLocalPath,servicePath);
                    //安装docker
                    String dockerName = FileUtils.getFileNameByPath(installDto.getDockerFile().getOriginalFilename());
                    String portainerName = FileUtils.getFileNameByPath(installDto.getPortainerFile().getOriginalFilename());
                    remoteSshUtil.exec(StringUtils.format("cd {} && tar -zxvf {}",installDto.getTempPath(),dockerName),session);
                    remoteSshUtil.exec(StringUtils.format("cp -p {}/docker/* /usr/bin",installDto.getTempPath()),session);
                    remoteSshUtil.exec("systemctl daemon-reload && systemctl restart  docker",session);
                    remoteSshUtil.exec("systemctl enable docker",session);
                    long startTime = System.currentTimeMillis();
                    while (true){
                        String exec = remoteSshUtil.exec("systemctl status docker", session);
                        if (StringUtils.isNotEmpty(exec) && exec.contains("running")) {
                            System.out.println("docker部署成功"+JacksonUtils.toString(admDocker));
                            break;
                        }
                        if (System.currentTimeMillis() -startTime>=30*1000){
                            throw new RuntimeException("docker启动已超过30秒，请检查配置是否正确");
                        }
                    }
                    Thread.sleep(2000);
                    //安装portainerUI
                    remoteSshUtil.exec(StringUtils.format("docker load -i  {}/{}",installDto.getTempPath(),portainerName),session);
                    DockerClient dockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(admDocker));
                    dockerUtil.delContainer(dockerClient, "portainer");
                    remoteSshUtil.exec(StringUtils.format("docker run -d --restart=always --name portainerUI -v /etc/localtime:/etc/localtime -p {}:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer",installDto.getPortainerPort()),session);
                    while (true) {
                        Thread.sleep(1000);
                        Boolean flag = false;
                        List<Container> containerList = dockerClient.listContainersCmd().exec();
                        for (Container container : containerList) {
                            if (container.getImage().contains("portainer") && "running".equals(container.getState())){
                                flag = true;
                            }
                        }
                        if (flag){
                            //注册
                            String initUrl = StringUtils.format("http://{}:{}/api/users/admin/init", serverDocker.getIp(), installDto.getPortainerPort());
                            HttpHeaders headers = new HttpHeaders();
                            headers.setContentType(MediaType.APPLICATION_JSON);
                            Map<String, Object> initMap = new HashMap(16);
                            initMap.put("Username", admDocker.getUsername());
                            initMap.put("Password", admDocker.getPassword());
                            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(initMap, headers);
                            restTemplate.postForObject(initUrl, entity, String.class);
                            System.out.println("注册账户成功"+JacksonUtils.toString(admDocker));
                            break;
                        }
                    }
                    admDocker.setInstallTime(new Date());
                    admDocker.setUpdateTime(new Date());
                    admDockerMapper.updateById(admDocker);
                    sendMsg("complete",installDto.getDockerFile().getOriginalFilename());
                    sendMsg("complete",installDto.getPortainerFile().getOriginalFilename());
                    System.out.println("portainerUI部署成功"+JacksonUtils.toString(admDocker));
                    //缓存
                    AdmDockerVo admDockerVo = BeanUtils.copy(admDocker, AdmDockerVo.class);
                    Server server = serverService.getServerById(admDocker.getServerId());
                    admDockerVo.setIp(server.getIp());
                    admDockerVo.setState("running");
                    String key = "admDockerVo"+admDocker.getId();
                    cacheUtils.setCacheObject(key,admDockerVo,1000*60*60);
                    dockerUtil.close(dockerClient);
                    remoteSshUtil.close(session);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(e.getMessage());
                    alertFailMsg(e.getMessage());
                } finally {
                    mainLatch.countDown();
                }
            });
        }
        try {
            mainLatch.await();
            alertSuccessMsg("completeAll");
            System.out.println("docker仓库全部安装完毕");
        }catch (InterruptedException e) {
            log.error(e.getMessage());
        }
    }

    @Override
    public List<AdmDockerVo> convertToVo(List<AdmDocker> list) {
        if (list == null || list.isEmpty()) {
            return new ArrayList<>();
        }
        List<AdmDockerVo> admDockerVos = new ArrayList<>();
        List<Server> serverList = serverService.getAll();
        for (AdmDocker admDocker : list) {
            String key = "admDockerVo"+admDocker.getId();
            if (cacheUtils.hasKey(key)){
                admDockerVos.add(cacheUtils.getCacheObject(key));
            }else {
                AdmDockerVo admDockerVo = BeanUtils.copy(admDocker, AdmDockerVo.class);
                Server server = serverList.stream().filter(s->s.getId().equals(admDocker.getServerId())).findFirst().orElse(null);
                admDockerVo.setIp(server.getIp());
                Session session = null;
                try {
                    //查询docker状态
                    session = remoteSshUtil.connect(server);
                    String exec = remoteSshUtil.exec("systemctl status docker", session);
                    if (StringUtils.isNotEmpty(exec) && exec.contains("running")) {
                        admDockerVo.setState("running");
                    }else {
                        admDockerVo.setState("docker未安装");
                    }
                }catch (Exception e) {
                    log.error("连接虚拟机失败：{}", JacksonUtils.toString(server));
                    admDockerVo.setState("连接失败");
                }finally {
                    remoteSshUtil.close(session);
                }
                cacheUtils.setCacheObject(key,admDockerVo,1000*60*60);
                admDockerVos.add(admDockerVo);
            }
        }
        return admDockerVos;
    }

    @Override
    public AdmDockerVo convertToVo(AdmDocker admDocker) {
        List<AdmDocker> list = new ArrayList<>();
        if (admDocker != null) {
            list.add(admDocker);
            return convertToVo(list).get(0);
        }
        return null;
    }

    @Override
    public AdmDockerVo getVoById(Long id) {
        String key = "admDockerVo"+id;
        if (cacheUtils.hasKey(key)){
            return cacheUtils.getCacheObject(key);
        }else {
            AdmDocker admDocker = getById(id);
            AdmDockerVo admDockerVo = BeanUtils.copy(admDocker, AdmDockerVo.class);
            Server server = serverService.getServerById(admDocker.getServerId());
            admDockerVo.setIp(server.getIp());
            Session session = null;
            try {
                //查询docker状态
                session = remoteSshUtil.connect(server);
                String exec = remoteSshUtil.exec("systemctl status docker", session);
                if (StringUtils.isNotEmpty(exec) && exec.contains("running")) {
                    admDockerVo.setState("running");
                }else {
                    admDockerVo.setState("docker未安装");
                }
            }catch (Exception e) {
                log.error("连接虚拟机失败：{}", JacksonUtils.toString(server));
                admDockerVo.setState("连接失败");
            }finally {
                remoteSshUtil.close(session);
            }
            cacheUtils.setCacheObject(key,admDockerVo,1000*1000*3000*1000);
            return admDockerVo;
        }
    }

    @Override
    public void init() {
        List<AdmDocker> dockerList = getAll();
        try {
            Map<Integer, List<AdmDocker>> groupedList = StringUtils.groupList(dockerList, 5);
            CountDownLatch latch = new CountDownLatch(groupedList.size());
            groupedList.forEach((k,v)->{
                threadPoolTaskExecutor.execute(()->{
                    try {
                        List<AdmDockerVo> admDockerVos = convertToVo(v);
                        admDockerVos.forEach(dockerUtil::getDockerClientByDocker);
                    } finally {
                        latch.countDown(); // 每个线程完成后计数减一
                    }
                });
            });
            latch.await(); // 主线程等待所有子线程完成
        }catch (Exception e) {
            log.error(e.getMessage());
        }finally {
            System.out.println("====================所有docker状态已缓存成功===================");
        }
    }



    private void sendMsg(String status,String name){
        websocketEndPoint.send(WsSendDto.builder().type(0).table("dockerInstall").status(status).name(name).build());
    }

    /**
     * 弹窗提示
     * @param data
     */
    private void alertSuccessMsg(String data){
        websocketEndPoint.send(WsSendDto.builder().type(2).table("dockerInstall").data(data).build());
    }
    /**
     * 弹窗提示
     * @param data
     */
    private void alertFailMsg(String data){
        websocketEndPoint.send(WsSendDto.builder().type(3).table("dockerInstall").data(data).build());
    }

    /**
     * 仅刷新表格
     */
    private void reloadHtml(){
        websocketEndPoint.send(WsSendDto.builder().type(1).table("dockerInstall").build());
    }

    @Override
    public Map<String, List<Container>> getContainerMapByVo(List<AdmDockerVo> admDockers) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(admDockers.size());
        Map<String, List<Container>> containerMap = new ConcurrentHashMap<>();
        admDockers.stream().<Runnable>map(admDockerVo -> () -> {
            try {
                DockerClient dockerClient = dockerUtil.getDockerClientByDocker(admDockerVo);
                List<Container> containers = dockerUtil.getAllContainer(dockerClient);
                containerMap.put("AdmDockerVo" + admDockerVo.getId(), containers);
            } finally {
                // 任务完成，计数器减一
                latch.countDown();
            }
        }).forEach(threadPoolTaskExecutor::execute);
        // 等待所有任务完成
        latch.await();
        return containerMap;
    }

    @Override
    public Map<String, List<Container>> getContainerMap(List<AdmDocker> dockerList) throws InterruptedException {
        List<AdmDockerVo> admDockerVos = convertToVo(dockerList);
        return getContainerMapByVo(admDockerVos);
    }


}
